﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Sustainalytics.Synchronization.Interface;
using MongoDB.Driver;
using Sustainalytics.Entities;
using System.Collections.Concurrent;
using MongoDB.Bson;

namespace Sustainalytics.Governance.Synchronization
{
    public class CorporateGovernanceResearchImport
    {
        private readonly ILogger _logger;
        private readonly CorporateDataQuery _corporateDataQuery;
        private readonly CorporateGovernanceAssessmentThresholdsQuery _corporateGovernanceAssessmentThresholdsQuery;
        private readonly GovernanceIndicatorTemplatesQuery _governanceIndicatorTemplatesQuery;
        private readonly CorporateGovernanceResearchProfileQuery _corporateGovernanceResearchProfileQuery;
        private readonly SaveEntitiesAsync _saveEntitiesAsync;
        private readonly IMongoCollection<GovernanceSource> _governanceSourceCollection;
        private readonly IMongoCollection<GovernanceIssue> _governanceIssueCollection;
        public List<Guid> ActiveResearchCompanyIds { get; set; }

        public CorporateGovernanceResearchImport(ILogger logger,
            GovernanceIndicatorTemplatesQuery governanceIndicatorTemplatesQuery,
            CorporateDataQuery corporateDataQuery,
            CorporateGovernanceResearchProfileQuery corporateGovernanceResearchProfileQuery,
            CorporateGovernanceAssessmentThresholdsQuery corporateGovernanceAssessmentThresholdsQuery,
            SaveEntitiesAsync saveEntitiesAsync,
            IMongoCollection<GovernanceSource> governanceSourceCollection,
            IMongoCollection<GovernanceIssue> governanceIssueCollection)
        {
            _logger = logger;
            _governanceIndicatorTemplatesQuery = governanceIndicatorTemplatesQuery;
            _corporateDataQuery = corporateDataQuery;
            _corporateGovernanceAssessmentThresholdsQuery = corporateGovernanceAssessmentThresholdsQuery;
            _corporateGovernanceResearchProfileQuery = corporateGovernanceResearchProfileQuery;
            _saveEntitiesAsync = saveEntitiesAsync;
            _governanceSourceCollection = governanceSourceCollection;
            _governanceIssueCollection = governanceIssueCollection;
        }

        public async Task StartAsync()
        {
            var governanceIndicatorTemplates = _governanceIndicatorTemplatesQuery.Get();
            var indicatorTemplates = CorporateGovernanceEntityConverter.GetIndicatorTemplates(governanceIndicatorTemplates);
            var issueDictionary = indicatorTemplates
                    .GroupBy(x => x.IssueNumber)
                    .Select(x => new
                    {
                        Number = x.Key,
                        Name = x.FirstOrDefault()?.IssueName
                    })
                    .ToDictionary(x => x.Number, x => x.Name);

            var governanceCompanyProfiles = _corporateDataQuery.Get(CorporateGovernanceCompanyProfilesQuery.Get).ToDictionary(x => x.Id, x => x);

            ActiveResearchCompanyIds = governanceCompanyProfiles.Keys.ToList();


            var corporateGovernanceResearchProfiles = OrderAndRenameIssues(_corporateGovernanceResearchProfileQuery.Get(ActiveResearchCompanyIds), issueDictionary);
            var corporateGovernanceAssessmentThresholds = CorporateGovernanceEntityConverter.GetAssessmentThresholds(_corporateGovernanceAssessmentThresholdsQuery.Get());

            var governanceComputationResults = CorporateGovernanceEntityConverter.GetGovernanceComputationResults(corporateGovernanceResearchProfiles);
            var governanceIssues = CorporateGovernanceEntityConverter.GetGovernanceIssues(
                corporateGovernanceResearchProfiles,
                governanceComputationResults.ToDictionary(x => x.Id, x => x.IssuesPerformance.ToDictionary(y => y.Number, y => y.Score)),
                governanceIndicatorTemplates.ToDictionary(x => x.Number));

            var governanceSources = CorporateGovernanceEntityConverter.GetGovernanceSources(governanceIssues);

            var governanceIndicators = governanceIssues.SelectMany(gi => gi.Indicators).ToList();

            await Task.WhenAll(new[]
            {
                _saveEntitiesAsync.ExecuteAsync(indicatorTemplates),
                _saveEntitiesAsync.ExecuteAsync(corporateGovernanceResearchProfiles),
                _saveEntitiesAsync.ExecuteAsync(governanceComputationResults),
                _saveEntitiesAsync.ExecuteAsync(corporateGovernanceAssessmentThresholds),
                _saveEntitiesAsync.ExecuteAsync(governanceIssues, CorporateGovernanceIndexes.RecreateIndexes),
                _saveEntitiesAsync.ExecuteAsync(governanceSources, CorporateGovernanceIndexes.RecreateIndexes),
                _saveEntitiesAsync.ExecuteAsync(governanceIndicators, CorporateGovernanceIndexes.RecreateIndexes),
            })
            .ConfigureAwait(false);

        }

        private IList<SharedEntities.CorporateGovernanceResearch.CorporateGovernanceResearchProfile> OrderAndRenameIssues(
            IList<SharedEntities.CorporateGovernanceResearch.CorporateGovernanceResearchProfile> profiles,
            IDictionary<string, string> issueDictionary)
        {
            Parallel.ForEach(profiles, new ParallelOptions() { MaxDegreeOfParallelism = (int)(Environment.ProcessorCount * 0.75) }, profile =>
            {
                profile.GovernanceIssues = profile.GovernanceIssues.OrderBy(x => x.Number);
                Parallel.ForEach(profile.GovernanceIssues, new ParallelOptions() { MaxDegreeOfParallelism = (int)(Environment.ProcessorCount * 0.75) }, issue =>
                {
                    issue.Name = issueDictionary.ContainsKey(issue.Number) ? issueDictionary[issue.Number] : issue.Name;
                });

                profile.GovernanceComputationResult.IssuesPerformance = profile.GovernanceComputationResult.IssuesPerformance.OrderBy(x => x.Number);
                Parallel.ForEach(profile.GovernanceComputationResult.IssuesPerformance, new ParallelOptions() { MaxDegreeOfParallelism = (int)(Environment.ProcessorCount * 0.75) }, issuePerformance =>
                {
                    issuePerformance.Name = issueDictionary.ContainsKey(issuePerformance.Number) ? issueDictionary[issuePerformance.Number] : issuePerformance.Name;
                });

            });

            return profiles;
        }
    }
}
